#include "HdmiInputSource.h"
#include "Util/logc.h"
namespace Media{

    typedef struct{
        std::weak_ptr<HdmiInputSource> m_source;
    }VideoInFrameCbArg;

    static VideoInFrameCbArg args = {};

    HdmiInputSource::HdmiInputSource():InputSource(HDMI, VIDEO, "hdmi", false) {
        m_videoIn = nullptr;
        m_frameConsumerIndex = 0;
    }

    HdmiInputSource::~HdmiInputSource() = default;

    bool HdmiInputSource::OpenSource() {
        std::lock_guard<std::recursive_mutex> guard(m_mux);
        if(m_videoIn){
            return true;
        }
        VideoInDesc desc = {};
        desc.type = videoInHdmi;
        if(createVideoInput(&desc, &m_videoIn) != 0 || !m_videoIn){
            errorf("createVideoInput hdmi fail");
            return false;
        }
        args.m_source = shared_from_this();

        m_videoIn->setFrameCb(m_videoIn, [](void* frame, void *arg){
            auto *args = (VideoInFrameCbArg *)arg;
            if(args){
                auto source = args->m_source.lock();
                if(source){
                    source->frameProc(frame);
                }
            }
        }, &args);
        if(m_videoIn->start(m_videoIn) != 0){
            errorf("hdmi VideoInput fail");
            return false;
        }
        tracef("hdmi VideoInput start ok");
        return true;
    }

    bool HdmiInputSource::CloseSource() {
//        if(m_videoIn){
//            m_videoIn->stop(m_videoIn);
//        }
        return true;
    }

    HdmiInputSource::Ptr HdmiInputSource::Instance() {
        static auto ptr = std::make_shared<HdmiInputSource>();

        return ptr;
    }

    bool HdmiInputSource::RemoveVFrameConsumer(InputSource::FrameConsumerId id) {
        std::lock_guard<std::recursive_mutex> guard(m_mux);
        if(m_frameConsumers.find(id) == m_frameConsumers.end()){
            return false;
        }
        m_frameConsumers.erase(id);
        return true;
    }

    InputSource::FrameConsumerId HdmiInputSource::AddVFrameConsumer(std::function<void(const AVFrame::Ptr)> framecb) {
        std::lock_guard<std::recursive_mutex> guard(m_mux);
        m_frameConsumerIndex++;
        FrameConsumerId id = m_frameConsumerIndex;
        m_frameConsumers[id] = std::move(framecb);
        return id;
    }

    void HdmiInputSource::frameProc(void *frame) {
        std::weak_ptr<HdmiInputSource> ptr = shared_from_this();
        auto avFrame = std::make_shared<AVFrame>(frame, true, [ptr](void *frame){
            auto shPtr = ptr.lock();
            if(shPtr){
                shPtr->ReleaseFrame(frame);
            }
        });

        std::lock_guard<std::recursive_mutex> guard(m_mux);
        for (const auto &item: m_frameConsumers){
            item.second(avFrame);
        }
    }

    void HdmiInputSource::ReleaseFrame(void *frame) {
        if(m_videoIn){
            m_videoIn->releaseFrame(m_videoIn, frame);
        }
    }
}